﻿using Argotic.Common;
using Argotic.Syndication;
using EnetaMvc.ApplicationServices;
using EnetaMvc.ApplicationServices.Dto;
using EnetaMvc.Infrastructure;
using log4net;
using StructureMap;
using System;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace EnetaMvc.FeedAggregator
{
    internal class FeedClient
    {
        private  INewsService _newsService;
        private readonly ILog _log;

        private const int FeedItemContentMaxLength = 255;

        public FeedClient()
        {
            ObjectFactory.Initialize(container =>
            {
                //container.PullConfigurationFromAppConfig = true;
                container.For<ILog>().Use(LogManager.GetLogger(GetType()));
            });

            _log = ObjectFactory.GetInstance<ILog>();
            _newsService = ObjectFactory.GetInstance<INewsService>();
        }

        public void Execute()
        {
            var connectionStrings = ConfigurationManager.ConnectionStrings;

            var mapperSession = ObjectFactory.GetInstance<IMapperSession>();

            foreach (ConnectionStringSettings connectionString in connectionStrings)
            {
                Debug.WriteLine("Switching to connection: " + connectionString.Name);
                mapperSession.SwitchToConnection(connectionString.ConnectionString);
                _newsService = ObjectFactory.GetInstance<INewsService>();
                //Debug.WriteLine("Hash: " + _newsService.GetHashCode());
                var blogs = _newsService.ListPublishedBlogs();

                var actions = new Task[blogs.Count];

                Debug.WriteLine("Blogs count: " + blogs.Count);
                
                for (var index = 0; index < blogs.Count; index++)
                {
                    actions[index] = new Task(ImportFeed, blogs[index]);
                    actions[index].Start();
                }

                Task.WaitAll(actions);
            }
        }

        private void ImportFeed(object blogObject)
        {
            var blog = (BlogDto)blogObject;
            if (string.IsNullOrEmpty(blog.RssUrl))
            {
                _log.Info("Cannot import blog " + blog.Name + ", no rss url given");
                return;
            }

            var uri = new Uri(blog.RssUrl);
            SyndicationContentFormat feedFormat;

            try
            {
                feedFormat = SyndicationDiscoveryUtility.SyndicationContentFormatGet(uri);
            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex);
                _log.Error("Error reading blog feed: " + blog.RssUrl, ex);
                return;
            }
            if (feedFormat == SyndicationContentFormat.Rss)
                ImportRssFeed(blog);
            if (feedFormat == SyndicationContentFormat.Atom)
                ImportAtomFeed(blog);
            
        }

        private void ImportRssFeed(BlogDto blog)
        {
            _log.Info("Importing feed " + blog.RssUrl);

            var uri = new Uri(blog.RssUrl);
            var feed = RssFeed.Create(uri);

            feed.Channel.Items.AsParallel().ForAll(a =>
            {
                Debug.WriteLine("Importing: " + a.Link);
                ImportRssFeedItem(a, blog.Id, blog.CreatedById);
            });
        }

        private void ImportRssFeedItem(RssItem item, long blogId, long blogOwnerId)
        {
            var newsService = ObjectFactory.GetInstance<INewsService>();
            if (newsService.BlogPostExists(item.Link.ToString(), blogId))
            {
                Debug.WriteLine("Exists, returning");
                return;
            }

            var blogPost = new BlogPostDto();
            blogPost.Abstract = GetBlogPostAbstract(item.Description);
            blogPost.BlogId = blogId;
            blogPost.Body = blogPost.Abstract;
            blogPost.Created = item.PublicationDate;
            blogPost.CreatedById = blogOwnerId;
            blogPost.InfoSourceUrl = item.Link.ToString();
            blogPost.PublishStateById = blogOwnerId;
            blogPost.PublishStateDate = DateTime.Now;
            blogPost.PublishStateId = 2;
            blogPost.Title = item.Title;

            newsService.SaveBlogPost(blogPost);
        }

        private void ImportAtomFeed(BlogDto blog)
        {
            var uri = new Uri(blog.RssUrl);
            var feed = AtomFeed.Create(uri);

            feed.Entries.AsParallel().ForAll(a =>
            {
                ImportAtomFeedEntry(a, blog.Id, blog.CreatedById);
            });
        }

        private void ImportAtomFeedEntry(AtomEntry item, long blogId, long blogOwnerId)
        {
            var url = item.Links.First(l => l.Relation.ToLower() == "alternate").Uri.ToString();

            var newsService = ObjectFactory.GetInstance<INewsService>();
            if (newsService.BlogPostExists(url, blogId))
                return;

            var blogPost = new BlogPostDto();
            if(item.Content == null)
                blogPost.Abstract = GetBlogPostAbstract(string.Empty);
            else 
                blogPost.Abstract = GetBlogPostAbstract(item.Content.Content);
            blogPost.BlogId = blogId;
            blogPost.Body = blogPost.Abstract;
            blogPost.Created = item.PublishedOn;
            blogPost.CreatedById = blogOwnerId;
            blogPost.InfoSourceUrl = url;
            blogPost.PublishStateById = blogOwnerId;
            blogPost.PublishStateDate = DateTime.Now;
            blogPost.PublishStateId = 2;
            blogPost.Title = item.Title.Content;

            newsService.SaveBlogPost(blogPost);
        }

        private string GetBlogPostAbstract(string description)
        {
            if (description.Length > FeedItemContentMaxLength)
                description = description.Substring(0, FeedItemContentMaxLength - 4);
            if (description.LastIndexOf(' ') > 0)
                description = description.Substring(0, description.LastIndexOf(' ')) + " ...";
            return description;
        }
    }
}
